home *** CD-ROM | disk | FTP | other *** search
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: README patchlevel.h Makefile uncgi.c
- # Wrapped by koreth@gerbilmeister on Wed Oct 19 00:50:17 1994
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(7639 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- X
- X UN-CGI VERSION 1.2
- X
- XContents
- X
- X * Introduction
- X * Installation
- X * Usage
- X * Other Features
- X * Bugs
- X * Frequently Asked Questions
- X
- XIntroduction
- X
- X
- X
- X This is uncgi 1.1, a frontend for processing queries and forms from
- X the Web on UNIX systems. You can get it via anonymous ftp from
- X ftp.hyperion.com or, depending on your browser, by following this
- X link.
- X
- X Without this program, if you wanted to process a form, you'd have to
- X either write or dig up routines to translate the values of the form's
- X fields from "URL encoding" to whatever your program required. This was
- X a hassle in C, and a real pain in the shell, and you had do things
- X differently for GET and POST queries.
- X
- X Which is where uncgi comes in. It decodes all the form fields and
- X sticks them into environment variables for easy perusal by a shell
- X script, a C program, a Perl script, or whatever you like, then
- X executes whatever other program you specify.
- X
- X (Actually, "uncgi" is something of a misnomer, as the weird URL syntax
- X is from the HTML forms specification, not from CGI itself.)
- X
- XInstallation
- X
- X
- X
- X To install, edit the Makefile, setting "CGI_BIN" to point to your HTTP
- X server's CGI directory. Under NCSA httpd, this is usually a directory
- X called "cgi-bin" in the server root directory. Then run "make
- X install".
- X
- X Note that the variable serves two purposes as supplied: it tells the
- X Makefile where to install uncgi, and it also tells uncgi where to look
- X for backend scripts. If you would like uncgi to look in a directory
- X other than the one it's installed in, set DESTDIR to the install
- X directory and CGI_BIN to the directory containing the scripts and
- X programs that uncgi will be executing.
- X
- XUsage
- X
- X
- X
- X An example is the easiest way to demonstrate uncgi's use. Suppose you
- X have the following in an HTML file:
- X
- X<form method=POST action="/cgi-bin/uncgi/myscript/thanks.html">
- XWhat's your name?
- X<input type=text size=30 name=name>
- X<p>
- XType some comments.
- X<br>
- X<textarea name=comments rows=10 cols=60></textarea>
- XWhat problem are you having? <select name=problem multiple>
- X<option> Can't sleep
- X<option> Unruly goat
- X<option> Limousine overcrowding
- X</select>
- X<p>
- X<input type=submit value=" Send 'em in! ">
- X</form>
- X
- X When the user selects the "Send 'em in!" button, the HTTP server will
- X run uncgi. Uncgi will set three environment variables, WWW_name,
- X WWW_comments and WWW_problem, to the values of the "name", "comments",
- X and "problem" fields in the form, respectively. Then it will execute
- X myscript in the CGI_BIN directory. If more than one "problem" is
- X selected, the values will all be placed in WWW_comments, separated by
- X hash marks ('#').
- X
- X All the usual CGI environment variables (PATH_INFO, QUERY_STRING,
- X etc.) are available to the script or program you tell uncgi to run. A
- X couple of them (PATH_INFO and PATH_TRANSLATED) are tweaked by uncgi to
- X the values they'd have if your program were being executed directly by
- X the server. In the above example, when uncgi ran myscript, PATH_INFO
- X would be set to "/thanks.html". This is an easy way to specify
- X additional parameters to your script without resorting to hidden
- X fields.
- X
- X Myscript might be as simple as this:
- X
- X#!/bin/sh
- Xecho 'Content-type: text/html'
- Xecho ''
- Xmail webmaster << __EOF__
- X$WWW_name said:
- X$WWW_comments
- X__EOF__
- Xcat $PATH_TRANSLATED
- X
- X With uncgi, that's all you need to do to write a script to send you
- X mail from a form and print a prewritten file as a response. And it's
- X the same whether you want to use GET or POST queries.
- X
- XOther Features
- X
- X
- X
- X Extra feature: If you compile with -DNO_MAIN, you can use uncgi as a
- X library function in a C program of your own. Just call uncgi() at the
- X start of your program.
- X
- X Uncgi will handle hybrid GET/POST requests. Specify a method of POST
- X in the form, and add a GET-style query string to the action, for
- X example <form method=POST
- X action="/cgi-bin/uncgi/myscript?form_id=feedback">. When your script
- X is run, WWW_form_id will be set to "feedback". This will only work if
- X your HTTP server supports it (NCSA's does, for now anyway.)
- X
- XBugs
- X
- X
- X
- X There should be a way to specify a list of directories that uncgi will
- X search for backend scripts.
- X
- XFrequently Asked Questions
- X
- X WHERE DO I PUT EVERYTHING?
- X
- X
- X
- X Short answer: Your server's cgi-bin directory.
- X
- X Long answer: When you edit uncgi's Makefile, you'll see two macro
- X definitions. The first, CGI_BIN, tells uncgi where to look for your
- X scripts or programs. It must be set to the name of an existing
- X directory; you can set it to any directory you like. Usually it's set
- X to the location of your server's cgi-bin directory.
- X
- X The second, DESTDIR, tells the Makefile where to install uncgi. By
- X default, it's set to the same thing CGI_BIN is. It must point to your
- X server's cgi-bin directory, so you'll need to change it if you set
- X CGI_BIN to something else.
- X
- X WHY DOES UNCGI TELL ME IT CAN'T RUN MY SCRIPT?
- X
- X
- X
- X First, make sure your script is in the right place; see the preceding
- X section.
- X
- X Second, make sure your script can be executed by the server. Remember,
- X the server probably isn't running from your account, so you need to
- X set the permissions on your script such that it can be run by any
- X user. Usually, you can say "chmod 755 scriptname" (replacing
- X scriptname with the name of your script) to set the permissions
- X properly.
- X
- X WHAT DO I DO IN MY FORMS?
- X
- X
- X
- X The <form> tag has two attributes, METHOD and ACTION. METHOD must be
- X set to either GET or POST; uncgi will handle either one, but POST is
- X preferred if you have textarea fields or are expecting a lot of
- X information from the client. Note that the attribute name (METHOD) can
- X be upper or lower case, but the value must be all caps.
- X
- X ACTION should have the following components:
- X * The alias for your server's cgi-bin directory (usually just
- X /cgi-bin/)
- X * uncgi/ to tell the server which program to execute.
- X * The name of your script as it appears in the directory where you
- X told uncgi to find your scripts and programs (the CGI_BIN macro in
- X the Makefile.) Example: myscript
- X * Optionally, a forward slash followed by additional parameters,
- X often the path to another file you want your script to print. This
- X path information will be available to your script in the PATH_INFO
- X and PATH_TRANSLATED environment variables (the latter contains the
- X full path to the document, including the path to the server root
- X directory.) Note that you can't use tilde notation. Example:
- X /form_output/acknowledge.html
- X
- X
- X
- X So, if you wanted to tell uncgi to run sendmemail with no additional
- X parameters, you'd put sendmemail in the directory you specified as the
- X value of CGI_BIN in uncgi's makefile, and use the following tag to
- X begin your form:
- X
- X <form method=POST action="/cgi-bin/uncgi/sendmemail">
- X
- X I GET AN ERROR CODE 500 FROM THE SERVER.
- X
- X
- X
- X This usually means your script isn't specifying a content type to the
- X server. The first thing your script needs to output is:
- X
- X Content-type: text/html
- X
- X followed by a blank line.
- X _________________________________________________________________
- X
- X Maintained by Steven Grimm <koreth@hyperion.com>.
- X Send mail if you have comments or suggestions.
- END_OF_FILE
- if test 7639 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'patchlevel.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'patchlevel.h'\"
- else
- echo shar: Extracting \"'patchlevel.h'\" \(22 characters\)
- sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
- X#define VERSION "1.2"
- END_OF_FILE
- if test 22 -ne `wc -c <'patchlevel.h'`; then
- echo shar: \"'patchlevel.h'\" unpacked with wrong size!
- fi
- # end of 'patchlevel.h'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(521 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- X#
- X# @(#)Makefile 1.2 8/21/94
- X#
- X# Makefile for "uncgi"
- X#
- X
- X#
- X# Set this to the directory where your CGI scripts live. Uncgi will look
- X# there for backend scripts.
- X#
- XCGI_BIN = /home/gerbilmeister/www/cgi-bin
- X
- X#
- X# Set this to the directory you'd like to install uncgi in, if different
- X# from CGI_BIN.
- X#
- XDESTDIR = $(CGI_BIN)
- X
- Xuncgi: uncgi.c
- X $(CC) $(CFLAGS) -DCGI_BIN='"$(CGI_BIN)"' -o $@ uncgi.c
- X
- Xinstall: uncgi
- X cp uncgi $(DESTDIR)
- X chmod 755 $(DESTDIR)/uncgi
- X
- Xshar:
- X shar README patchlevel.h Makefile uncgi.c > uncgi.shar
- END_OF_FILE
- if test 521 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test -f 'uncgi.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'uncgi.c'\"
- else
- echo shar: Extracting \"'uncgi.c'\" \(8558 characters\)
- sed "s/^X//" >'uncgi.c' <<'END_OF_FILE'
- X/*
- X * @(#)uncgi.c 1.7 10/19/94
- X *
- X * Unescape all the fields in a form and stick them in the environment
- X * so they can be used without awful machinations.
- X *
- X * Call with an ACTION such as:
- X * http://foo.bar.com/cgi-bin/uncgi/myscript/extra/path/stuff
- X *
- X * Uncgi will run "myscript" from the cgi-bin directory, and set PATH_INFO
- X * to "/extra/path/stuff".
- X *
- X * Environment variable names are "WWW_" plus the field name.
- X *
- X * Copyright 1994, Steven Grimm <koreth@hyperion.com>.
- X *
- X * Permission is granted to redistribute freely and use for any purpose,
- X * commercial or private, so long as this copyright notice is retained
- X * and the source code is included free of charge with any binary
- X * distributions.
- X */
- X#include <stdio.h>
- X#include <ctype.h>
- X
- Xvoid *malloc(), *realloc();
- Xchar *getenv();
- X
- Xextern char *sys_errlist[];
- Xextern int errno;
- X
- X#define PREFIX "WWW_"
- X#define ishex(x) (((x) >= '0' && (x) <= '9') || ((x) >= 'a' && (x) <= 'f') || \
- X ((x) >= 'A' && (x) <= 'F'))
- X
- X/*
- X * Convert two hex digits to a value.
- X */
- Xstatic int
- Xhtoi(s)
- X unsigned char *s;
- X{
- X int value;
- X char c;
- X
- X c = s[0];
- X if (isupper(c))
- X c = tolower(c);
- X value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
- X
- X c = s[1];
- X if (isupper(c))
- X c = tolower(c);
- X value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
- X
- X return (value);
- X}
- X
- X/*
- X * Get rid of all the URL escaping in a string. Modify it in place, since
- X * the result will always be equal in length or smaller.
- X */
- Xstatic void
- Xurl_unescape(str)
- X unsigned char *str;
- X{
- X unsigned char *dest = str;
- X
- X while (str[0])
- X {
- X if (str[0] == '+')
- X dest[0] = ' ';
- X else if (str[0] == '%' && ishex(str[1]) && ishex(str[2]))
- X {
- X dest[0] = (unsigned char) htoi(str + 1);
- X str += 2;
- X }
- X else
- X dest[0] = str[0];
- X
- X str++;
- X dest++;
- X }
- X
- X dest[0] = '\0';
- X}
- X
- X/*
- X * Print the start of an error message.
- X */
- Xstatic void
- Xhttp_head()
- X{
- X puts("Content-type: text/html\n");
- X puts("<html><head><title>Error!</title></head><body>");
- X puts("<h1>An error has occurred while processing your request.</h1>");
- X}
- X
- X/*
- X * Print an HTML error string with the right HTTP header and exit.
- X */
- Xstatic void
- Xhtml_perror(str)
- X char *str;
- X{
- X http_head();
- X
- X puts("<p>The following error was encountered while processing");
- X puts("your query.");
- X puts("<blockquote>");
- X printf("%s: %s\n", str, sys_errlist[errno]);
- X puts("</blockquote>");
- X puts("<p>Please try again later.");
- X puts("</body></html>");
- X
- X exit(1);
- X}
- X
- X/*
- X * Stuff a URL-unescaped variable, with the prefix on its name, into the
- X * environment. Uses the "=" from the CGI arguments. Putting an "=" in
- X * a field name is probably a bad idea.
- X *
- X * If the variable is already defined, append a '#' to it along with the
- X * new value.
- X */
- Xstatic void
- Xstuffenv(var)
- X char *var;
- X{
- X char *buf, *c, *oldval, *newval;
- X
- X url_unescape(var);
- X
- X /*
- X * Allocate enough memory for the variable name and its value.
- X */
- X buf = malloc(strlen(var) + sizeof(PREFIX) + 1);
- X if (buf == NULL)
- X html_perror("stuffenv");
- X
- X strcpy(buf, PREFIX);
- X strcpy(buf + sizeof(PREFIX) - 1, var); /* save a few cycles */
- X
- X /*
- X * If, for some reason, there wasn't an = in the query string,
- X * add one so the environment will be valid.
- X */
- X for (c = buf; *c != '\0'; c++)
- X if (*c == '=')
- X break;
- X if (*c == '\0')
- X c[1] = '\0';
- X
- X /*
- X * Check for the presence of the variable.
- X */
- X *c = '\0';
- X if (oldval = getenv(buf))
- X {
- X newval = malloc(strlen(oldval) + strlen(buf) + strlen(c+1) + 2);
- X if (newval == NULL)
- X html_perror("stuffenv: append");
- X
- X *c = '=';
- X strcpy(newval, buf);
- X strcat(newval, "#");
- X strcat(newval, oldval);
- X free(buf);
- X }
- X else
- X {
- X *c = '=';
- X newval = buf;
- X }
- X
- X putenv(newval);
- X}
- X
- X/*
- X * Scan a query string, stuffing variables into the environment. This
- X * should ideally just use strtok(), but that's not available everywhere.
- X */
- Xstatic void
- Xscanquery(q)
- X char *q;
- X{
- X char *next = q;
- X
- X do {
- X while (*next && *next != '&')
- X next++;
- X if (! *next)
- X next = NULL;
- X else
- X *next = '\0';
- X
- X stuffenv(q);
- X if (next)
- X *next++ = '&';
- X q = next;
- X } while (q != NULL);
- X}
- X
- X/*
- X * Read a POST query from standard input into a dynamic buffer. Terminate
- X * it with a null character.
- X */
- Xstatic char *
- Xpostread()
- X{
- X char *buf = NULL;
- X int size = 0, sofar = 0, got;
- X
- X buf = getenv("CONTENT_TYPE");
- X if (buf == NULL || strcmp(buf, "application/x-www-form-urlencoded"))
- X {
- X http_head();
- X puts("<p>No content type was passed to uncgi.</body></html>");
- X exit(1);
- X }
- X
- X buf = getenv("CONTENT_LENGTH");
- X if (buf == NULL)
- X {
- X http_head();
- X puts("<p>The server did not tell uncgi how long the request");
- X puts("was.</body></html>");
- X exit(1);
- X }
- X
- X size = atoi(buf);
- X buf = malloc(size);
- X if (buf == NULL)
- X html_perror("postread");
- X do
- X {
- X got = fread(buf + sofar, 1, size - sofar, stdin);
- X sofar += got;
- X } while (got && sofar < size);
- X
- X buf[sofar] = '\0';
- X
- X return (buf);
- X}
- X
- X/*
- X * Run a shell script. We use this instead of the OS's "#!" mechanism
- X * because that mechanism doesn't work too well on SVR3-based systems.
- X */
- Xvoid
- Xrunscript(shell, script)
- X char *shell, *script;
- X{
- X char *argvec[4], *space;
- X int pos;
- X
- X if (shell[0] != '/')
- X return;
- X
- X pos = strlen(shell) - 1;
- X if (shell[pos] == '\n')
- X shell[pos] = '\0';
- X
- X argvec[0] = shell;
- X
- X /*
- X * See if there's an argument string. strchr() isn't available
- X * everywhere, so do it ourselves.
- X */
- X for (space = shell; *space; space++)
- X if (*space == ' ')
- X break;
- X
- X if (*space)
- X {
- X *space = '\0';
- X argvec[1] = space + 1;
- X argvec[2] = script;
- X argvec[3] = NULL;
- X }
- X else
- X {
- X argvec[1] = script;
- X argvec[2] = NULL;
- X }
- X
- X execv(shell, argvec);
- X /* Fall back to main() on error. */
- X}
- X
- X/*
- X * Main program, optionally callable as a library function.
- X */
- Xvoid
- X#ifdef NO_MAIN
- Xuncgi()
- X#else
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X#endif
- X{
- X char *query, *program, *pathinfo, *newpathinfo, *method;
- X char *ptrans, *ptend;
- X char *argvec[2], shellname[200];
- X int proglen;
- X FILE *fp;
- X
- X /*
- X * First, get the query string, wherever it is, and stick its
- X * component parts into the environment. Allow combination
- X * GET and POST queries, even though that's a bit strange.
- X */
- X query = getenv("QUERY_STRING");
- X if (query != NULL && strlen(query))
- X scanquery(query);
- X
- X method = getenv("REQUEST_METHOD");
- X if (method != NULL && ! strcmp(method, "POST"))
- X {
- X query = postread();
- X if (query[0] != '\0')
- X scanquery(query);
- X }
- X
- X if (query == NULL)
- X {
- X http_head();
- X puts("<p>The 'uncgi' program couldn't find a query to");
- X puts("process.\n</body></html>");
- X exit(1);
- X }
- X
- X#ifndef NO_MAIN
- X /*
- X * Now figure out which program the caller *really* wants, and adjust
- X * PATH_INFO and PATH_TRANSLATED to look right to that program.
- X */
- X pathinfo = getenv("PATH_INFO");
- X if (pathinfo != NULL && pathinfo[0])
- X {
- X proglen = 1; /* path_info always starts with '/' */
- X
- X while (pathinfo[proglen] && pathinfo[proglen] != '/')
- X proglen++;
- X
- X program = malloc(proglen + sizeof(CGI_BIN));
- X if (program == NULL)
- X html_perror("program");
- X strcpy(program, CGI_BIN);
- X strncat(program + sizeof(CGI_BIN) - 1, pathinfo, proglen);
- X
- X /*
- X * Strip "program" from PATH_TRANSLATED.
- X * XXX - this depends on strcpy() copying front to back.
- X */
- X ptrans = getenv("PATH_TRANSLATED");
- X if (ptrans != NULL)
- X {
- X ptend = ptrans + strlen(ptrans) - strlen(pathinfo);
- X strcpy(ptend, ptend + proglen);
- X ptend = malloc(strlen(ptrans) +
- X sizeof("PATH_TRANSLATED="));
- X strcpy(ptend, "PATH_TRANSLATED=");
- X strcat(ptend, ptrans);
- X putenv(ptend);
- X }
- X
- X pathinfo += proglen;
- X newpathinfo = malloc(strlen(pathinfo) + sizeof("PATH_INFO="));
- X if (newpathinfo == NULL)
- X html_perror("newpathinfo");
- X strcpy(newpathinfo, "PATH_INFO=");
- X strcat(newpathinfo, pathinfo);
- X putenv(newpathinfo);
- X
- X
- X }
- X else
- X {
- X /*
- X * No PATH_INFO means no program to run.
- X */
- X http_head();
- X puts("<p>Whoever wrote this form doesn't know how to use");
- X puts("the 'uncgi' program, because they didn't tell it");
- X puts("what to run.");
- X puts("<h5>(Bummer.)</h5></body></html>");
- X
- X exit(0);
- X }
- X
- X /*
- X * SVR3-based systems seem to have trouble running shell scripts.
- X * So if that's what this is, run its shell explicitly.
- X */
- X fp = fopen(program, "r");
- X if (fp != NULL)
- X {
- X if (fgets(shellname, sizeof(shellname), fp) != NULL)
- X {
- X fclose(fp);
- X if (shellname[0] == '#' && shellname[1] == '!')
- X runscript(shellname + 2, program);
- X }
- X fclose(fp);
- X }
- X
- X /*
- X * Now execute the program.
- X */
- X argvec[0] = program;
- X argvec[1] = NULL;
- X execv(program, argvec);
- X
- X /*
- X * If we get here, the exec failed.
- X */
- X html_perror(program);
- X#endif
- X}
- END_OF_FILE
- if test 8558 -ne `wc -c <'uncgi.c'`; then
- echo shar: \"'uncgi.c'\" unpacked with wrong size!
- fi
- # end of 'uncgi.c'
- fi
- echo shar: End of shell archive.
- exit 0
-